package org.tlipoca.smsteamstore.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.genntii.smcommon.constant.ExceptionConstant;
import org.genntii.smcommon.exception.EsException;
import org.genntii.smcommon.result.PageResult;
import org.genntii.smdomain.doc.ArticleDoc;
import org.genntii.smdomain.doc.SteamStoreDoc;
import org.genntii.smdomain.entity.SteamAppDetail;
import org.genntii.smdomain.entity.SteamStore;
import org.genntii.smdomain.vo.SteamStoreDocVO;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.tlipoca.smsteamstore.service.EsService;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class EsServiceImpl implements EsService {

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private MongoTemplate mongoTemplate;

    @Override
    public void insertSteamStore(SteamStore steamStore) {
        SteamStoreDoc steamStoreDoc = SteamStoreDoc.builder()
                .id(steamStore.getId())
                .appid(steamStore.getAppid())
                .name(steamStore.getName())
                .headerImage(steamStore.getHeaderImage())
                .capsuleImage(steamStore.getCapsuleImage())
                .detailDescription(steamStore.getDetailDescription())
                .shortDescription(steamStore.getShortDescription())
                .isFree(steamStore.isFree())
                .requiredAge(steamStore.getRequiredAge())
                .isDlc(steamStore.isDlc())
                .fullGameAppid(steamStore.getFullGameAppid())
                .build();

        try {
            IndexRequest request = new IndexRequest();
            request.index("steam_store").id(String.valueOf(steamStoreDoc.getId()));

            String steamStoreJson = objectMapper.writeValueAsString(steamStoreDoc);

            request.source(steamStoreJson, XContentType.JSON);
            IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
            log.info(String.valueOf(response.getResult()));

        }catch (IOException e){
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION);
        }
    }

    @Override
    public void deleteSteamStore(SteamStore steamStore) {
        try {
            DeleteRequest request = new DeleteRequest();
            request.index("steam_store").id(String.valueOf(steamStore.getId()));

            DeleteResponse response = restHighLevelClient.delete(request,RequestOptions.DEFAULT);
            log.info(String.valueOf(response.getResult()));

        }catch (IOException e){
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION );
        }
    }

    @Override
    public PageResult<SteamStoreDocVO> search(String keyword, Integer pageNum, Integer pageSize) {
        SearchRequest request = new SearchRequest();
        request.indices("steam_store");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.from((pageNum - 1) * 10);
        builder.size(10);

        builder.query(QueryBuilders.matchQuery("name",keyword));

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        HighlightBuilder.Field highLightName = new HighlightBuilder.Field("name");
        highlightBuilder.field(highLightName);
        highlightBuilder.preTags("<font color = '#dd4b39'>");
        highlightBuilder.postTags("</font>");

        builder.highlighter(highlightBuilder);
        request.source(builder);
        SearchResponse response;

        try {
            response = restHighLevelClient.search(request,RequestOptions.DEFAULT);
        }catch (IOException e){
            throw new EsException(ExceptionConstant.ELASTIC_SEARCH_HANDLE_EXCEPTION);
        }
        ArrayList<Integer> steamStoreIds = new ArrayList<>();
        Map<Integer,SteamStoreDoc> steamStoreDocs = new HashMap<>();
        for (SearchHit hit:response.getHits().getHits()){
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField nameHighlight = highlightFields.get("name");
            String highLightedName = null;

            if (nameHighlight != null){
                Text[] fragments = nameHighlight.fragments();
                highLightedName = Arrays.stream(fragments)
                        .map(Text::string)
                        .collect(Collectors.joining());
            }

            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            SteamStoreDoc steamStoreDoc = JSON.parseObject(JSON.toJSONString(sourceAsMap), SteamStoreDoc.class);
            if (highLightedName != null){
                steamStoreDoc.setName(highLightedName);
            }

            steamStoreIds.add(steamStoreDoc.getAppid());
            steamStoreDocs.put(steamStoreDoc.getAppid(), steamStoreDoc);
        }

        List<SteamAppDetail> steamAppDetailList = mongoTemplate.find(new Query(new Criteria("steam_appid").in(steamStoreIds)), SteamAppDetail.class,"SteamAppDetail");
        Map<Integer ,SteamAppDetail> steamAppDetailMap = new HashMap<>();
        for (SteamAppDetail steamAppDetail : steamAppDetailList){
            steamAppDetailMap.put(steamAppDetail.getSteam_appid(),steamAppDetail);
        }
        ArrayList<SteamStoreDocVO> steamStoreDocVOS = new ArrayList<>();
        for (Integer id : steamStoreIds){
            steamStoreDocVOS.add(SteamStoreDocVO.builder()
                    .steamStoreDoc(steamStoreDocs.get(id))
                    .steamAppDetail(steamAppDetailMap.get(id))
                    .build());
        }

        return PageResult.build(steamStoreDocVOS,response.getHits().getTotalHits().value);
    }
}
